home *** CD-ROM | disk | FTP | other *** search
/ Action Games (2008) / akcnihry1.iso / Code RED / codered.exe / Data1.cab / g_weapon.c1 < prev    next >
Encoding:
Text File  |  2002-12-26  |  40.9 KB  |  1,561 lines

  1. #include "g_local.h"
  2.  
  3.  
  4. /*
  5. =================
  6. check_dodge
  7.  
  8. This is a support routine used when a client is firing
  9. a non-instant attack weapon.  It checks to see if a
  10. monster's dodge function should be called.
  11. =================
  12. */
  13. static void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed)
  14. {
  15.     vec3_t    end;
  16.     vec3_t    v;
  17.     trace_t    tr;
  18.     float    eta;
  19.  
  20.     // easy mode only ducks one quarter the time
  21.     if (skill->value == 0)
  22.     {
  23.         if (random() > 0.25)
  24.             return;
  25.     }
  26.     VectorMA (start, 8192, dir, end);
  27.     tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT);
  28.     if ((tr.ent) && (tr.ent->svflags & SVF_MONSTER) && (tr.ent->health > 0) && (tr.ent->monsterinfo.dodge) && infront(tr.ent, self))
  29.     {
  30.         VectorSubtract (tr.endpos, start, v);
  31.         eta = (VectorLength(v) - tr.ent->maxs[0]) / speed;
  32.         tr.ent->monsterinfo.dodge (tr.ent, self, eta);
  33.     }
  34. }
  35.  
  36.  
  37. /*
  38. =================
  39. fire_hit
  40.  
  41. Used for all impact (hit/punch/slash) attacks
  42. =================
  43. */
  44. qboolean fire_hit (edict_t *self, vec3_t aim, int damage, int kick)
  45. {
  46.     trace_t        tr;
  47.     vec3_t        forward, right, up;
  48.     vec3_t        v;
  49.     vec3_t        point;
  50.     float        range;
  51.     vec3_t        dir;
  52.  
  53.     //see if enemy is in range
  54.     VectorSubtract (self->enemy->s.origin, self->s.origin, dir);
  55.     range = VectorLength(dir);
  56.     if (range > aim[0])
  57.         return false;
  58.  
  59.     if (aim[1] > self->mins[0] && aim[1] < self->maxs[0])
  60.     {
  61.         // the hit is straight on so back the range up to the edge of their bbox
  62.         range -= self->enemy->maxs[0];
  63.     }
  64.     else
  65.     {
  66.         // this is a side hit so adjust the "right" value out to the edge of their bbox
  67.         if (aim[1] < 0)
  68.             aim[1] = self->enemy->mins[0];
  69.         else
  70.             aim[1] = self->enemy->maxs[0];
  71.     }
  72.  
  73.     VectorMA (self->s.origin, range, dir, point);
  74.  
  75.     tr = gi.trace (self->s.origin, NULL, NULL, point, self, MASK_SHOT);
  76.     if (tr.fraction < 1)
  77.     {
  78.         if (!tr.ent->takedamage)
  79.             return false;
  80.         // if it will hit any client/monster then hit the one we wanted to hit
  81.         if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client))
  82.             tr.ent = self->enemy;
  83.     }
  84.  
  85.     AngleVectors(self->s.angles, forward, right, up);
  86.     VectorMA (self->s.origin, range, forward, point);
  87.     VectorMA (point, aim[1], right, point);
  88.     VectorMA (point, aim[2], up, point);
  89.     VectorSubtract (point, self->enemy->s.origin, dir);
  90.  
  91.     // do the damage
  92.     T_Damage (tr.ent, self, self, dir, point, vec3_origin, damage, kick/2, DAMAGE_NO_KNOCKBACK, MOD_HIT);
  93.  
  94.     if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
  95.         return false;
  96.  
  97.     // do our special form of knockback here
  98.     VectorMA (self->enemy->absmin, 0.5, self->enemy->size, v);
  99.     VectorSubtract (v, point, v);
  100.     VectorNormalize (v);
  101.     VectorMA (self->enemy->velocity, kick, v, self->enemy->velocity);
  102.     if (self->enemy->velocity[2] > 0)
  103.         self->enemy->groundentity = NULL;
  104.     return true;
  105. }
  106.  
  107.  
  108. /*
  109. =================
  110. fire_lead
  111.  
  112. This is an internal support routine used for bullet/pellet based weapons.
  113. =================
  114. */
  115. static void fire_lead (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread, int mod)
  116. {
  117.     trace_t        tr;
  118.     vec3_t        dir;
  119.     vec3_t        forward, right, up;
  120.     vec3_t        end;
  121.     float        r;
  122.     float        u;
  123.     vec3_t        water_start;
  124.     qboolean    water = false;
  125.     int            content_mask = MASK_SHOT | MASK_WATER;
  126.  
  127.     tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT);
  128.     if (!(tr.fraction < 1.0))
  129.     {
  130.         vectoangles (aimdir, dir);
  131.         AngleVectors (dir, forward, right, up);
  132.  
  133.         r = crandom()*hspread;
  134.         u = crandom()*vspread;
  135.         VectorMA (start, 8192, forward, end);
  136.         VectorMA (end, r, right, end);
  137.         VectorMA (end, u, up, end);
  138.  
  139.         if (gi.pointcontents (start) & MASK_WATER)
  140.         {
  141.             water = true;
  142.             VectorCopy (start, water_start);
  143.             content_mask &= ~MASK_WATER;
  144.         }
  145.  
  146.         tr = gi.trace (start, NULL, NULL, end, self, content_mask);
  147.  
  148.         // see if we hit water
  149.         if (tr.contents & MASK_WATER)
  150.         {
  151.             int        color;
  152.  
  153.             water = true;
  154.             VectorCopy (tr.endpos, water_start);
  155.  
  156.             if (!VectorCompare (start, tr.endpos))
  157.             {
  158.                 if (tr.contents & CONTENTS_WATER)
  159.                 {
  160.                     if (strcmp(tr.surface->name, "*brwater") == 0)
  161.                         color = SPLASH_BROWN_WATER;
  162.                     else
  163.                         color = SPLASH_BLUE_WATER;
  164.                 }
  165.                 else if (tr.contents & CONTENTS_SLIME)
  166.                     color = SPLASH_SLIME;
  167.                 else if (tr.contents & CONTENTS_LAVA)
  168.                     color = SPLASH_LAVA;
  169.                 else
  170.                     color = SPLASH_UNKNOWN;
  171.  
  172.                 if (color != SPLASH_UNKNOWN)
  173.                 {
  174.                     gi.WriteByte (svc_temp_entity);
  175.                     gi.WriteByte (TE_SPLASH);
  176.                     gi.WriteByte (8);
  177.                     gi.WritePosition (tr.endpos);
  178.                     gi.WriteDir (tr.plane.normal);
  179.                     gi.WriteByte (color);
  180.                     gi.multicast (tr.endpos, MULTICAST_PVS);
  181.                 }
  182.  
  183.                 // change bullet's course when it enters water
  184.                 VectorSubtract (end, start, dir);
  185.                 vectoangles (dir, dir);
  186.                 AngleVectors (dir, forward, right, up);
  187.                 r = crandom()*hspread*2;
  188.                 u = crandom()*vspread*2;
  189.                 VectorMA (water_start, 8192, forward, end);
  190.                 VectorMA (end, r, right, end);
  191.                 VectorMA (end, u, up, end);
  192.             }
  193.  
  194.             // re-trace ignoring water this time
  195.             tr = gi.trace (water_start, NULL, NULL, end, self, MASK_SHOT);
  196.         }
  197.     }
  198.  
  199.     // send gun puff / flash
  200.     if (!((tr.surface) && (tr.surface->flags & SURF_SKY)))
  201.     {
  202.         if (tr.fraction < 1.0)
  203.         {
  204.             if (tr.ent->takedamage)
  205.             {
  206.                 T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_BULLET, mod);
  207.             }
  208.             else
  209.             {
  210.                 if (strncmp (tr.surface->name, "sky", 3) != 0)
  211.                 {
  212.                     gi.WriteByte (svc_temp_entity);
  213.                     gi.WriteByte (te_impact);
  214.                     gi.WritePosition (tr.endpos);
  215.                     gi.WriteDir (tr.plane.normal);
  216.                     gi.multicast (tr.endpos, MULTICAST_PVS);
  217.  
  218.                     if (self->client)
  219.                         PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
  220.                 }
  221.             }
  222.         }
  223.     }
  224.  
  225.     // if went through water, determine where the end and make a bubble trail
  226.     if (water)
  227.     {
  228.         vec3_t    pos;
  229.  
  230.         VectorSubtract (tr.endpos, water_start, dir);
  231.         VectorNormalize (dir);
  232.         VectorMA (tr.endpos, -2, dir, pos);
  233.         if (gi.pointcontents (pos) & MASK_WATER)
  234.             VectorCopy (pos, tr.endpos);
  235.         else
  236.             tr = gi.trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER);
  237.  
  238.         VectorAdd (water_start, tr.endpos, pos);
  239.         VectorScale (pos, 0.5, pos);
  240.  
  241.         gi.WriteByte (svc_temp_entity);
  242.         gi.WriteByte (TE_BUBBLETRAIL);
  243.         gi.WritePosition (water_start);
  244.         gi.WritePosition (tr.endpos);
  245.         gi.multicast (pos, MULTICAST_PVS);
  246.     }
  247. }
  248.  
  249.  
  250. /*
  251. =================
  252. fire_bullet
  253.  
  254. Fires a single round.  Used for machinegun and chaingun.  Would be fine for
  255. pistols, rifles, etc....
  256. =================
  257. */
  258. void fire_bullet (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int mod)
  259. {
  260.     fire_lead (self, start, aimdir, damage, kick, TE_GUNSHOT, hspread, vspread, mod);
  261. }
  262.  
  263.  
  264. /*
  265. =================
  266. fire_shotgun
  267.  
  268. Shoots shotgun pellets.  Used by shotgun and super shotgun.
  269. =================
  270. */
  271. void fire_shotgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int mod)
  272. {
  273.     int        i;
  274.  
  275.     for (i = 0; i < count; i++)
  276.         fire_lead (self, start, aimdir, damage, kick, TE_SHOTGUN, hspread, vspread, mod);
  277. }
  278.  
  279.  
  280. /*
  281. =================
  282. fire_blaster
  283. Fires a single blaster bolt.  Used by the blaster and hyper blaster.
  284. =================
  285. */
  286. void blaster_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
  287. {
  288.     int        mod;
  289.  
  290.     if (other == self->owner)
  291.         return;
  292.  
  293.     if (surf && (surf->flags & SURF_SKY))
  294.     {
  295.         G_FreeEdict (self);
  296.         return;
  297.     }
  298.  
  299.     if (self->owner->client)
  300.         PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
  301.  
  302.     if (other->takedamage)
  303.     {
  304.         if (self->spawnflags & 1)
  305.             mod = MOD_HYPERBLASTER;
  306.         else
  307.             mod = MOD_BLASTER;
  308.         T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod);
  309.     }
  310.     else
  311.     {
  312.         gi.WriteByte (svc_temp_entity);
  313.         gi.WriteByte (TE_BLASTER);
  314.         gi.WritePosition (self->s.origin);
  315.         if (!plane)
  316.             gi.WriteDir (vec3_origin);
  317.         else
  318.             gi.WriteDir (plane->normal);
  319.         gi.multicast (self->s.origin, MULTICAST_PVS);
  320.     }
  321.  
  322.     G_FreeEdict (self);
  323. }
  324.  
  325. void fire_blaster (edict_t *self, vec3_t start, vec3_t aimdir, int damage,
  326. int speed, int effect, qboolean hyper)
  327. {   
  328.     vec3_t        from;
  329.     vec3_t        end;
  330.     trace_t        tr;
  331.     edict_t        *ignore;
  332.     int            mask;
  333.     qboolean    water;
  334.  
  335.     VectorMA (start, 8192, aimdir, end);
  336.     VectorCopy (start, from);
  337.     ignore = self;
  338.     water = false;
  339.     mask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA;
  340.     while (ignore)
  341.     {
  342.         tr = gi.trace (from, NULL, NULL, end, ignore, mask);
  343.  
  344.         if (tr.contents & (CONTENTS_SLIME|CONTENTS_LAVA))
  345.         {
  346.             mask &= ~(CONTENTS_SLIME|CONTENTS_LAVA);
  347.             water = true;
  348.         }
  349.         else
  350.         {
  351.             if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client))
  352.                 ignore = tr.ent;
  353.             else
  354.                 ignore = NULL;
  355.  
  356.             if ((tr.ent != self) && (tr.ent->takedamage))
  357.                 T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, 0, 0, MOD_RAILGUN);
  358.         }
  359.  
  360.         VectorCopy (tr.endpos, from);
  361.     }
  362.  
  363.     VectorMA (start, 8192, aimdir, end);
  364.     VectorCopy (start, from);
  365.  
  366.     // trace for end point of laser beam.      // the laser aim is perfect. 
  367.     // no random aim like the machinegun
  368.     tr = gi.trace (from, NULL, NULL, end, self, MASK_SHOT);      
  369.  
  370.     // send laser beam temp entity to clients
  371.     VectorCopy (tr.endpos, from);
  372.  
  373.     gi.WriteByte (svc_temp_entity);
  374.     gi.WriteByte (TE_SHOTGUN);
  375.     gi.WritePosition (start);
  376.     gi.WritePosition (tr.endpos);
  377.     gi.multicast (self->s.origin, MULTICAST_PHS);   
  378.       
  379.     if ((tr.ent != self) && (tr.ent->takedamage))
  380.         T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, 0, 0, MOD_HYPERBLASTER);
  381.     else if (!((tr.surface) && (tr.surface->flags & SURF_SKY)))
  382.     {  
  383.         gi.WriteByte (svc_temp_entity);
  384.         gi.WriteByte (TE_SCREEN_SPARKS);
  385.         gi.WritePosition (tr.endpos);
  386.         gi.WriteDir (tr.plane.normal);
  387.         gi.multicast (self->s.origin, MULTICAST_PVS);
  388.  
  389.     }
  390.    
  391. }       
  392.  
  393.  
  394.  
  395. /*
  396. =================
  397. fire_grenade
  398. =================
  399. */
  400. static void Grenade_Explode (edict_t *ent)
  401. {
  402.     vec3_t        origin;
  403.     int            mod;
  404.  
  405.     if (ent->owner->client)
  406.         PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
  407.  
  408.     //FIXME: if we are onground then raise our Z just a bit since we are a point?
  409.     if (ent->enemy)
  410.     {
  411.         float    points;
  412.         vec3_t    v;
  413.         vec3_t    dir;
  414.  
  415.         VectorAdd (ent->enemy->mins, ent->enemy->maxs, v);
  416.         VectorMA (ent->enemy->s.origin, 0.5, v, v);
  417.         VectorSubtract (ent->s.origin, v, v);
  418.         points = ent->dmg - 0.5 * VectorLength (v);
  419.         VectorSubtract (ent->enemy->s.origin, ent->s.origin, dir);
  420.         if (ent->spawnflags & 1)
  421.             mod = MOD_HANDGRENADE;
  422.         else
  423.             mod = MOD_GRENADE;
  424.         T_Damage (ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
  425.     }
  426.  
  427.     if (ent->spawnflags & 2)
  428.         mod = MOD_HELD_GRENADE;
  429.     else if (ent->spawnflags & 1)
  430.         mod = MOD_HG_SPLASH;
  431.     else
  432.         mod = MOD_G_SPLASH;
  433.     T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, mod);
  434.  
  435.     VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
  436.     gi.WriteByte (svc_temp_entity);
  437.     if (ent->waterlevel)
  438.     {
  439.         if (ent->groundentity)
  440.             gi.WriteByte (TE_GRENADE_EXPLOSION_WATER);
  441.         else
  442.             gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
  443.     }
  444.     else
  445.     {
  446.         if (ent->groundentity)
  447.             gi.WriteByte (TE_GRENADE_EXPLOSION);
  448.         else
  449.             gi.WriteByte (TE_ROCKET_EXPLOSION);
  450.     }
  451.     gi.WritePosition (origin);
  452.     gi.multicast (ent->s.origin, MULTICAST_PHS);
  453.  
  454.     G_FreeEdict (ent);
  455. }
  456.  
  457. static void Grenade_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
  458. {
  459.     if (other == ent->owner)
  460.         return;
  461.  
  462.     if (surf && (surf->flags & SURF_SKY))
  463.     {
  464.         G_FreeEdict (ent);
  465.         return;
  466.     }
  467.  
  468.     if (!other->takedamage)
  469.     {
  470.         if (ent->spawnflags & 1)
  471.         {
  472.             if (random() > 0.5)
  473.                 gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
  474.             else
  475.                 gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
  476.         }
  477.         else
  478.         {
  479.             gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0);
  480.         }
  481.         return;
  482.     }
  483.  
  484.     ent->enemy = other;
  485.     Grenade_Explode (ent);
  486. }
  487.  
  488. void fireball_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
  489. {
  490.     vec3_t        origin;
  491.     int            n;
  492.  
  493.     if (other == ent->owner)
  494.         return;
  495.  
  496.     if (surf && (surf->flags & SURF_SKY))
  497.     {
  498.         G_FreeEdict (ent);
  499.         return;
  500.     }
  501.  
  502.     if (ent->owner->client)
  503.         PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
  504.  
  505.     // calculate position for the explosion entity
  506.     VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
  507.  
  508.     if (other->takedamage)
  509.     {
  510.         T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_ROCKET);
  511.     }
  512.     else
  513.     {
  514.         // don't throw any debris in net games
  515.         if (!deathmatch->value && !coop->value)
  516.         {
  517.             if ((surf) && !(surf->flags & (SURF_WARP|SURF_TRANS33|SURF_TRANS66|SURF_FLOWING)))
  518.             {
  519.                 n = rand() % 5;
  520.                 while(n--)
  521.                     ThrowDebris (ent, "models/objects/debris2/tris.md2", 2, ent->s.origin);
  522.             }
  523.         }
  524.     }
  525.  
  526.     T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_R_SPLASH);
  527.  
  528.     gi.WriteByte (svc_temp_entity);
  529.     if (ent->waterlevel)
  530.         gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
  531.     else
  532.         gi.WriteByte (TE_ROCKET_EXPLOSION);
  533.     gi.WritePosition (origin);
  534.     gi.multicast (ent->s.origin, MULTICAST_PHS);
  535.  
  536.     G_FreeEdict (ent);
  537. }
  538.  
  539. void fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius)
  540. {
  541.     edict_t    *grenade;
  542.     vec3_t    dir;
  543.     vec3_t    forward, right, up;
  544.  
  545.     vectoangles (aimdir, dir);
  546.     AngleVectors (dir, forward, right, up);
  547.     up[2] = 0;
  548.     grenade = G_Spawn();
  549.     VectorCopy (start, grenade->s.origin);
  550.     VectorScale (aimdir, speed, grenade->velocity);
  551.     VectorMA (grenade->velocity, 400 + crandom() * 10.0, up, grenade->velocity);
  552.     VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
  553.     VectorSet (grenade->avelocity, 500, 500, 500);
  554.     grenade->movetype = MOVETYPE_BOUNCE;
  555.     grenade->clipmask = MASK_SHOT;
  556.     grenade->solid = SOLID_BBOX;
  557.     grenade->s.effects |= EF_SHIPEXHAUST;
  558.     VectorClear (grenade->mins);
  559.     VectorClear (grenade->maxs);
  560.     grenade->s.modelindex = gi.modelindex ("models/objects/fireball/tris.md2");
  561.     grenade->owner = self;
  562.     grenade->touch = fireball_touch;
  563.     grenade->nextthink = level.time + timer;
  564.     grenade->think = G_FreeEdict;
  565.     grenade->dmg = damage;
  566.     grenade->dmg_radius = damage_radius;
  567.     grenade->classname = "grenade";
  568.  
  569.     gi.linkentity (grenade);
  570. }
  571.  
  572. void fire_grenade2 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, qboolean held)
  573. {
  574.     edict_t    *grenade;
  575.     vec3_t    dir;
  576.     vec3_t    forward, right, up;
  577.  
  578.     vectoangles (aimdir, dir);
  579.     AngleVectors (dir, forward, right, up);
  580.  
  581.     grenade = G_Spawn();
  582.     VectorCopy (start, grenade->s.origin);
  583.     VectorScale (aimdir, speed, grenade->velocity);
  584.     VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
  585.     VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
  586.     VectorSet (grenade->avelocity, 300, 300, 300);
  587.     grenade->movetype = MOVETYPE_BOUNCE;
  588.     grenade->clipmask = MASK_SHOT;
  589.     grenade->solid = SOLID_BBOX;
  590.     grenade->s.effects |= EF_GRENADE;
  591.     VectorClear (grenade->mins);
  592.     VectorClear (grenade->maxs);
  593.     grenade->s.modelindex = gi.modelindex ("models/objects/grenade2/tris.md2");
  594.     grenade->owner = self;
  595.     grenade->touch = Grenade_Touch;
  596.     grenade->nextthink = level.time + timer;
  597.     grenade->think = G_FreeEdict;;
  598.     grenade->dmg = damage;
  599.     grenade->dmg_radius = damage_radius;
  600.     grenade->classname = "hgrenade";
  601.     if (held)
  602.         grenade->spawnflags = 3;
  603.     else
  604.         grenade->spawnflags = 1;
  605.     grenade->s.sound = gi.soundindex("weapons/hgrenc1b.wav");
  606.  
  607.     if (timer <= 0.0)
  608.         Grenade_Explode (grenade);
  609.     else
  610.     {
  611.         gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/hgrent1a.wav"), 1, ATTN_NORM, 0);
  612.         gi.linkentity (grenade);
  613.     }
  614. }
  615.  
  616.  
  617. /*
  618. =================
  619. fire_rocket
  620. =================
  621. */
  622. void rocket_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
  623. {
  624.     vec3_t        origin;
  625.     int            n;
  626.  
  627.     if (other == ent->owner)
  628.         return;
  629.  
  630.     if (surf && (surf->flags & SURF_SKY))
  631.     {
  632.         G_FreeEdict (ent);
  633.         return;
  634.     }
  635.  
  636.     if (ent->owner->client)
  637.         PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
  638.  
  639.     // calculate position for the explosion entity
  640.     VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
  641.  
  642.     if (other->takedamage)
  643.     {
  644.         T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_ROCKET);
  645.     }
  646.     else
  647.     {
  648.         // don't throw any debris in net games
  649.         if (!deathmatch->value && !coop->value)
  650.         {
  651.             if ((surf) && !(surf->flags & (SURF_WARP|SURF_TRANS33|SURF_TRANS66|SURF_FLOWING)))
  652.             {
  653.                 n = rand() % 5;
  654.                 while(n--)
  655.                     ThrowDebris (ent, "models/objects/debris2/tris.md2", 2, ent->s.origin);
  656.             }
  657.         }
  658.     }
  659.  
  660.     T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_R_SPLASH);
  661.  
  662.     gi.WriteByte (svc_temp_entity);
  663.     if (ent->waterlevel)
  664.         gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
  665.     else
  666.         gi.WriteByte (TE_ROCKET_EXPLOSION);
  667.     gi.WritePosition (origin);
  668.     gi.multicast (ent->s.origin, MULTICAST_PHS);
  669.  
  670.     G_FreeEdict (ent);
  671. }
  672.  
  673. void fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
  674. {
  675.     edict_t    *rocket;
  676.  
  677.     rocket = G_Spawn();
  678.     VectorCopy (start, rocket->s.origin);
  679.     VectorCopy (dir, rocket->movedir);
  680.     vectoangles (dir, rocket->s.angles);
  681.     VectorScale (dir, speed, rocket->velocity);
  682.     rocket->movetype = MOVETYPE_FLYMISSILE;
  683.     rocket->clipmask = MASK_SHOT;
  684.     rocket->solid = SOLID_BBOX;
  685.     rocket->s.effects |= EF_ROCKET;
  686.     VectorClear (rocket->mins);
  687.     VectorClear (rocket->maxs);
  688.     rocket->s.modelindex = gi.modelindex ("models/objects/rocket/tris.md2");
  689.     rocket->owner = self;
  690.     rocket->touch = rocket_touch;
  691.     rocket->nextthink = level.time + 8000/speed;
  692.     rocket->think = G_FreeEdict;
  693.     rocket->dmg = damage;
  694.     rocket->radius_dmg = radius_damage;
  695.     rocket->dmg_radius = damage_radius;
  696.     rocket->s.sound = gi.soundindex ("weapons/rockfly.wav");
  697.     rocket->classname = "rocket";
  698.  
  699.     if (self->client)
  700.         check_dodge (self, rocket->s.origin, dir, speed);
  701.  
  702.     gi.linkentity (rocket);
  703. }
  704.  
  705. /*
  706. =================
  707. fire_stinger
  708. =================
  709. */
  710. void stinger_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
  711. {
  712.     vec3_t        origin;
  713.     int            n;
  714.  
  715.     if (other == ent->owner)
  716.         return;
  717.  
  718.     if (surf && (surf->flags & SURF_SKY))
  719.     {
  720.         G_FreeEdict (ent);
  721.         return;
  722.     }
  723.  
  724.     if (ent->owner->client)
  725.         PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
  726.  
  727.     // calculate position for the explosion entity
  728.     VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
  729.  
  730.     if (other->takedamage)
  731.     {
  732.         T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_ROCKET);
  733.     }
  734.     else
  735.     {
  736.         // don't throw any debris in net games
  737.         if (!deathmatch->value && !coop->value)
  738.         {
  739.             if ((surf) && !(surf->flags & (SURF_WARP|SURF_TRANS33|SURF_TRANS66|SURF_FLOWING)))
  740.             {
  741.                 n = rand() % 5;
  742.                 while(n--)
  743.                     ThrowDebris (ent, "models/objects/debris2/tris.md2", 2, ent->s.origin);
  744.             }
  745.         }
  746.     }
  747.  
  748.     T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_R_SPLASH);
  749.  
  750.     gi.WriteByte (svc_temp_entity);
  751.     if (ent->waterlevel)
  752.         gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
  753.     else
  754.         gi.WriteByte (TE_ROCKET_EXPLOSION);
  755.     gi.WritePosition (origin);
  756.     gi.multicast (ent->s.origin, MULTICAST_PHS);
  757.  
  758.     G_FreeEdict (ent);
  759. }
  760.  
  761. void fire_stinger (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
  762. {
  763.     edict_t    *stinger;
  764.  
  765.     stinger = G_Spawn();
  766.     VectorCopy (start, stinger->s.origin);
  767.     VectorCopy (dir, stinger->movedir);
  768.     vectoangles (dir, stinger->s.angles);
  769.     VectorScale (dir, speed, stinger->velocity);
  770.     stinger->movetype = MOVETYPE_FLYMISSILE;
  771.     stinger->clipmask = MASK_SHOT;
  772.     stinger->solid = SOLID_BBOX;
  773.     stinger->s.effects |= EF_ROCKET;
  774.     VectorClear (stinger->mins);
  775.     VectorClear (stinger->maxs);
  776.     stinger->s.modelindex = gi.modelindex ("models/objects/stinger/tris.md2");
  777.     stinger->owner = self;
  778.     stinger->touch = stinger_touch;
  779.     stinger->nextthink = level.time + 8000/speed;
  780.     stinger->think = G_FreeEdict;
  781.     stinger->dmg = damage;
  782.     stinger->radius_dmg = radius_damage;
  783.     stinger->dmg_radius = damage_radius;
  784.     stinger->s.sound = gi.soundindex ("weapons/rockfly.wav");
  785.     stinger->classname = "stinger";
  786.  
  787.     if (self->client)
  788.         check_dodge (self, stinger->s.origin, dir, speed);
  789.  
  790.     gi.linkentity (stinger);
  791. }
  792.  
  793. /*
  794. =================
  795. fire_rail
  796. =================
  797. */
  798. void fire_rail (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick)
  799. {
  800.     vec3_t        from;
  801.     vec3_t        end;
  802.     trace_t        tr;
  803.     edict_t        *ignore;
  804.     int            mask;
  805.     qboolean    water;
  806.  
  807.     VectorMA (start, 8192, aimdir, end);
  808.     VectorCopy (start, from);
  809.     ignore = self;
  810.     water = false;
  811.     mask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA;
  812.     while (ignore)
  813.     {
  814.         tr = gi.trace (from, NULL, NULL, end, ignore, mask);
  815.  
  816.         if (tr.contents & (CONTENTS_SLIME|CONTENTS_LAVA))
  817.         {
  818.             mask &= ~(CONTENTS_SLIME|CONTENTS_LAVA);
  819.             water = true;
  820.         }
  821.         else
  822.         {
  823.             if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client))
  824.                 ignore = tr.ent;
  825.             else
  826.                 ignore = NULL;
  827.  
  828.             if ((tr.ent != self) && (tr.ent->takedamage))
  829.                 T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_RAILGUN);
  830.         }
  831.  
  832.         VectorCopy (tr.endpos, from);
  833.     }
  834.  
  835.     VectorMA (start, 8192, aimdir, end);
  836.     VectorCopy (start, from);
  837.  
  838.     // trace for end point of laser beam.      // the laser aim is perfect. 
  839.     // no random aim like the machinegun
  840.     tr = gi.trace (from, NULL, NULL, end, self, MASK_SHOT);      
  841.  
  842.     // send laser beam temp entity to clients
  843.     VectorCopy (tr.endpos, from);
  844.  
  845.     gi.WriteByte (svc_temp_entity);
  846.     gi.WriteByte (TE_PARASITE_ATTACK);
  847.     gi.WritePosition (start);
  848.     gi.WritePosition (tr.endpos);
  849.     gi.multicast (self->s.origin, MULTICAST_PHS);   
  850.     
  851.     gi.WriteByte (svc_temp_entity);
  852.     gi.WriteByte (TE_EXPLOSION1);
  853.     gi.WritePosition (tr.endpos);
  854.     gi.multicast (tr.endpos, MULTICAST_PVS);
  855.  
  856.     if (self->client)
  857.         PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
  858. }
  859.  
  860. void fire_blaster_beam (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick)
  861. {
  862.     vec3_t        from;
  863.     vec3_t        end;
  864.     trace_t        tr;
  865.     edict_t        *ignore;
  866.     int            mask;
  867.     qboolean    water;
  868.     
  869.     VectorMA (start, 8192, aimdir, end);
  870.     VectorCopy (start, from);
  871.     ignore = self;
  872.     water = false;
  873.     mask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA;
  874.     while (ignore)
  875.     {
  876.         tr = gi.trace (from, NULL, NULL, end, ignore, mask);
  877.         
  878.         if (tr.contents & (CONTENTS_SLIME|CONTENTS_LAVA))
  879.         {
  880.             mask &= ~(CONTENTS_SLIME|CONTENTS_LAVA);
  881.             water = true;
  882.         }
  883.         else
  884.         {
  885.             if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client))
  886.                 ignore = tr.ent;
  887.             else
  888.                 ignore = NULL;
  889.             
  890.             if ((tr.ent != self) && (tr.ent->takedamage))
  891.                 T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_BLASTER);
  892.         }
  893.         
  894.         VectorCopy (tr.endpos, from);
  895.     }
  896.  
  897.     VectorMA (start, 8192, aimdir, end);
  898.     VectorCopy (start, from);
  899.  
  900.     // trace for end point of laser beam.      // the laser aim is perfect. 
  901.     // no random aim like the machinegun
  902.     tr = gi.trace (from, NULL, NULL, end, self, MASK_SHOT);  
  903.     
  904.     // send laser beam temp entity to clients
  905.     VectorCopy (tr.endpos, from);
  906.  
  907.     gi.WriteByte (svc_temp_entity);
  908.     gi.WriteByte (TE_BLASTERBEAM);
  909.     gi.WritePosition (start);
  910.     gi.WritePosition (tr.endpos);
  911.     gi.multicast (self->s.origin, MULTICAST_PHS);   
  912.     
  913.     gi.WriteByte (svc_temp_entity);
  914.     gi.WriteByte (TE_BLASTER);
  915.     gi.WritePosition (tr.endpos);
  916.     gi.WriteDir (vec3_origin);
  917.     gi.multicast (tr.endpos, MULTICAST_PVS);
  918.  
  919.     if (self->client)
  920.         PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
  921. }
  922.  
  923. void fire_plasma (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick)
  924. {
  925.     vec3_t        from;
  926.     vec3_t        end;
  927.     trace_t        tr;
  928.     edict_t        *ignore;
  929.     int            mask;
  930.     qboolean    water;
  931.  
  932.     VectorMA (start, 8192, aimdir, end);
  933.     VectorCopy (start, from);
  934.     ignore = self;
  935.     water = false;
  936.     mask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA;
  937.     while (ignore)
  938.     {
  939.         tr = gi.trace (from, NULL, NULL, end, ignore, mask);
  940.  
  941.         if (tr.contents & (CONTENTS_SLIME|CONTENTS_LAVA))
  942.         {
  943.             mask &= ~(CONTENTS_SLIME|CONTENTS_LAVA);
  944.             water = true;
  945.         }
  946.         else
  947.         {
  948.             if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client))
  949.                 ignore = tr.ent;
  950.             else
  951.                 ignore = NULL;
  952.  
  953.             if ((tr.ent != self) && (tr.ent->takedamage))
  954.                 T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_RAILGUN);
  955.         }
  956.  
  957.         VectorCopy (tr.endpos, from);
  958.     }
  959.  
  960.     VectorMA (start, 8192, aimdir, end);
  961.     VectorCopy (start, from);
  962.  
  963.     // trace for end point of laser beam.      // the laser aim is perfect. 
  964.     // no random aim like the machinegun
  965.     tr = gi.trace (from, NULL, NULL, end, self, MASK_SHOT);     
  966.     
  967.     // send laser beam temp entity to clients
  968.     VectorCopy (tr.endpos, from);
  969.     
  970.     gi.WriteByte (svc_temp_entity);
  971.     gi.WriteByte (TE_RAILTRAIL);
  972.     gi.WritePosition (start);
  973.     gi.WritePosition (tr.endpos);
  974.     gi.multicast (self->s.origin, MULTICAST_PHS);   
  975.     
  976.     gi.WriteByte (svc_temp_entity);
  977.     gi.WriteByte (TE_MEDIC_CABLE_ATTACK);
  978.     gi.WritePosition (start);
  979.     gi.WritePosition (tr.endpos);
  980.     gi.multicast (self->s.origin, MULTICAST_PHS);   
  981.     
  982.     if (self->client)
  983.         PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
  984. }
  985.  
  986. /*
  987. =================
  988. fire_bfg
  989. =================
  990. */
  991. void bfg_explode (edict_t *self)
  992. {
  993.     edict_t    *ent;
  994.     float    points;
  995.     vec3_t    v;
  996.     float    dist;
  997.  
  998.     if (self->s.frame == 0)
  999.     {
  1000.         // the BFG effect
  1001.         ent = NULL;
  1002.         while ((ent = findradius(ent, self->s.origin, self->dmg_radius)) != NULL)
  1003.         {
  1004.             if (!ent->takedamage)
  1005.                 continue;
  1006.             if (ent == self->owner)
  1007.                 continue;
  1008.             if (!CanDamage (ent, self))
  1009.                 continue;
  1010.             if (!CanDamage (ent, self->owner))
  1011.                 continue;
  1012.  
  1013.             VectorAdd (ent->mins, ent->maxs, v);
  1014.             VectorMA (ent->s.origin, 0.5, v, v);
  1015.             VectorSubtract (self->s.origin, v, v);
  1016.             dist = VectorLength(v);
  1017.             points = self->radius_dmg * (1.0 - sqrt(dist/self->dmg_radius));
  1018.             if (ent == self->owner)
  1019.                 points = points * 0.5;
  1020.  
  1021.             gi.WriteByte (svc_temp_entity);
  1022.             gi.WriteByte (TE_BFG_EXPLOSION);
  1023.             gi.WritePosition (ent->s.origin);
  1024.             gi.multicast (ent->s.origin, MULTICAST_PHS);
  1025.             T_Damage (ent, self, self->owner, self->velocity, ent->s.origin, vec3_origin, (int)points, 0, DAMAGE_ENERGY, MOD_BFG_EFFECT);
  1026.         }
  1027.     }
  1028.  
  1029.     self->nextthink = level.time + FRAMETIME;
  1030.     self->s.frame++;
  1031.     if (self->s.frame == 5)
  1032.         self->think = G_FreeEdict;
  1033. }
  1034.  
  1035. void bfg_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
  1036. {
  1037.     if (other == self->owner)
  1038.         return;
  1039.  
  1040.     if (surf && (surf->flags & SURF_SKY))
  1041.     {
  1042.         G_FreeEdict (self);
  1043.         return;
  1044.     }
  1045.  
  1046.     if (self->owner->client)
  1047.         PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
  1048.  
  1049.     // core explosion - prevents firing it into the wall/floor
  1050.     if (other->takedamage)
  1051.         T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, 200, 0, 0, MOD_BFG_BLAST);
  1052.     T_RadiusDamage(self, self->owner, 200, other, 100, MOD_BFG_BLAST);
  1053.  
  1054.     gi.sound (self, CHAN_VOICE, gi.soundindex ("weapons/bfg__x1b.wav"), 1, ATTN_NORM, 0);
  1055.     self->solid = SOLID_NOT;
  1056.     self->touch = NULL;
  1057.     VectorMA (self->s.origin, -1 * FRAMETIME, self->velocity, self->s.origin);
  1058.     VectorClear (self->velocity);
  1059.     self->s.modelindex = gi.modelindex ("sprites/s_bfg3.sp2");
  1060.     self->s.frame = 0;
  1061.     self->s.sound = 0;
  1062.     self->s.effects &= ~EF_ANIM_ALLFAST;
  1063.     self->think = bfg_explode;
  1064.     self->nextthink = level.time + FRAMETIME;
  1065.     self->enemy = other;
  1066.  
  1067.     gi.WriteByte (svc_temp_entity);
  1068.     gi.WriteByte (TE_BFG_BIGEXPLOSION);
  1069.     gi.WritePosition (self->s.origin);
  1070.     gi.multicast (self->s.origin, MULTICAST_PVS);
  1071. }
  1072.  
  1073.  
  1074. void bfg_think (edict_t *self)
  1075. {
  1076.     edict_t    *ent;
  1077.     edict_t *target = NULL;
  1078.     edict_t    *ignore;
  1079.     vec3_t    point;
  1080.     vec3_t    dir, targetdir;
  1081.     vec3_t    start;
  1082.     vec3_t    end;
  1083.     vec_t   speed;
  1084.     int        dmg;
  1085.     trace_t    tr;
  1086.  
  1087.     if (deathmatch->value)
  1088.         dmg = 5;
  1089.     else
  1090.         dmg = 10;
  1091.  
  1092.     ent = NULL;
  1093.     while ((ent = findradius(ent, self->s.origin, 256)) != NULL)
  1094.     {
  1095.         if (ent == self)
  1096.             continue;
  1097.  
  1098.         if (ent == self->owner)
  1099.             continue;
  1100.  
  1101.         if (!ent->takedamage)
  1102.             continue;
  1103.  
  1104.         if (!(ent->svflags & SVF_MONSTER) && (!ent->client) && (strcmp(ent->classname, "misc_explobox") != 0))
  1105.             continue;
  1106.  
  1107.         VectorMA (ent->absmin, 0.5, ent->size, point);
  1108.  
  1109.         VectorSubtract (point, self->s.origin, dir);
  1110.         VectorNormalize (dir);
  1111.  
  1112.         ignore = self;
  1113.         VectorCopy (self->s.origin, start);
  1114.         VectorMA (start, 2048, dir, end);
  1115.         while(1)
  1116.         {
  1117.             tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
  1118.  
  1119.             if (!tr.ent)
  1120.                 break;
  1121.  
  1122.             // hurt it if we can
  1123.             if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && (tr.ent != self->owner))
  1124.                 T_Damage (tr.ent, self, self->owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER);
  1125.  
  1126.             // if we hit something that's not a monster or player we're done
  1127.             if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
  1128.             {
  1129.                 gi.WriteByte (svc_temp_entity);
  1130.                 gi.WriteByte (TE_LASER_SPARKS);
  1131.                 gi.WriteByte (4);
  1132.                 gi.WritePosition (tr.endpos);
  1133.                 gi.WriteDir (tr.plane.normal);
  1134.                 gi.WriteByte (self->s.skinnum);
  1135.                 gi.multicast (tr.endpos, MULTICAST_PVS);
  1136.                 break;
  1137.             }
  1138.  
  1139.             ignore = tr.ent;
  1140.             VectorCopy (tr.endpos, start);
  1141.         }
  1142.  
  1143.         gi.WriteByte (svc_temp_entity);
  1144.         gi.WriteByte (TE_LIGHTNING);
  1145.         gi.WritePosition (self->s.origin);
  1146.         gi.WritePosition (tr.endpos);
  1147.         gi.multicast (self->s.origin, MULTICAST_PHS);
  1148.  
  1149.         target = ent;
  1150.     }
  1151.  
  1152.     if (target != NULL)
  1153.     {
  1154.         // target acquired, nudge our direction toward it.
  1155.         VectorNormalize(targetdir);
  1156.         VectorScale(targetdir, 0.2, targetdir);
  1157.         VectorAdd(targetdir, self->movedir, targetdir);
  1158.         VectorNormalize(targetdir);
  1159.         VectorCopy(targetdir, self->movedir);
  1160.         vectoangles(targetdir, self->s.angles);
  1161.         speed = VectorLength(self->velocity);
  1162.         VectorScale(targetdir, speed, self->velocity);
  1163.     }
  1164.     
  1165.     self->nextthink = level.time + FRAMETIME;
  1166. }
  1167.  
  1168. void bfg_think_alien (edict_t *self)
  1169. {
  1170.     edict_t    *ent;
  1171.     edict_t *target = NULL;
  1172.     edict_t    *ignore;
  1173.     vec3_t    point;
  1174.     vec3_t    dir;
  1175.     vec3_t    start;
  1176.     vec3_t    end;
  1177.     int        dmg;
  1178.     trace_t    tr;
  1179.  
  1180.     if (deathmatch->value)
  1181.         dmg = 5;
  1182.     else
  1183.         dmg = 10;
  1184.  
  1185.     ent = NULL;
  1186.     while ((ent = findradius(ent, self->s.origin, 256)) != NULL)
  1187.     {
  1188.         if (ent == self)
  1189.             continue;
  1190.  
  1191.         if (ent == self->owner)
  1192.             continue;
  1193.  
  1194.         if (!ent->takedamage)
  1195.             continue;
  1196.  
  1197.         if (!(ent->svflags & SVF_MONSTER) && (!ent->client) && (strcmp(ent->classname, "misc_explobox") != 0))
  1198.             continue;
  1199.  
  1200.         if((ent->classname == "monster_martian") || (ent->classname == "monster_ambass"))
  1201.             continue;
  1202.  
  1203.         VectorMA (ent->absmin, 0.5, ent->size, point);
  1204.  
  1205.         VectorSubtract (point, self->s.origin, dir);
  1206.         VectorNormalize (dir);
  1207.  
  1208.         ignore = self;
  1209.         VectorCopy (self->s.origin, start);
  1210.         VectorMA (start, 2048, dir, end);
  1211.         while(1)
  1212.         {
  1213.             tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
  1214.  
  1215.             if (!tr.ent)
  1216.                 break;
  1217.  
  1218.             // hurt it if we can
  1219.             if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && (tr.ent != self->owner))
  1220.                 T_Damage (tr.ent, self, self->owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER);
  1221.  
  1222.             // if we hit something that's not a monster or player we're done
  1223.             if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
  1224.             {
  1225.                 gi.WriteByte (svc_temp_entity);
  1226.                 gi.WriteByte (TE_LASER_SPARKS);
  1227.                 gi.WriteByte (4);
  1228.                 gi.WritePosition (tr.endpos);
  1229.                 gi.WriteDir (tr.plane.normal);
  1230.                 gi.WriteByte (self->s.skinnum);
  1231.                 gi.multicast (tr.endpos, MULTICAST_PVS);
  1232.                 break;
  1233.             }
  1234.  
  1235.             ignore = tr.ent;
  1236.             VectorCopy (tr.endpos, start);
  1237.         }
  1238.  
  1239.         gi.WriteByte (svc_temp_entity);
  1240.         gi.WriteByte (TE_LIGHTNING);
  1241.         gi.WritePosition (self->s.origin);
  1242.         gi.WritePosition (tr.endpos);
  1243.         gi.multicast (self->s.origin, MULTICAST_PHS);
  1244.  
  1245.         target = ent;
  1246.     }
  1247.     
  1248.     self->nextthink = level.time + FRAMETIME;
  1249. }
  1250. void fire_bfg (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius)
  1251. {
  1252.     edict_t    *bfg;
  1253.  
  1254.     bfg = G_Spawn();
  1255.     VectorCopy (start, bfg->s.origin);
  1256.     VectorCopy (dir, bfg->movedir);
  1257.     vectoangles (dir, bfg->s.angles);
  1258.     VectorScale (dir, speed, bfg->velocity);
  1259.     bfg->movetype = MOVETYPE_FLYMISSILE;
  1260.     bfg->clipmask = MASK_SHOT;
  1261.     bfg->solid = SOLID_BBOX;
  1262.     bfg->s.effects |= EF_BFG | EF_ANIM_ALLFAST;
  1263.     VectorClear (bfg->mins);
  1264.     VectorClear (bfg->maxs);
  1265.     bfg->s.modelindex = gi.modelindex ("sprites/s_bfg1.sp2");
  1266.     bfg->owner = self;
  1267.     bfg->touch = bfg_touch;
  1268.     bfg->nextthink = level.time + 8000/speed;
  1269.     bfg->think = G_FreeEdict;
  1270.     bfg->radius_dmg = damage;
  1271.     bfg->dmg_radius = damage_radius;
  1272.     bfg->classname = "bfg blast";
  1273.     bfg->s.sound = gi.soundindex ("weapons/bfg__l1a.wav");
  1274.  
  1275.     bfg->think = bfg_think;
  1276.     bfg->nextthink = level.time + FRAMETIME;
  1277.     bfg->teammaster = bfg;
  1278.     bfg->teamchain = NULL;
  1279.  
  1280.     if (self->client)
  1281.         check_dodge (self, bfg->s.origin, dir, speed);
  1282.  
  1283.     gi.linkentity (bfg);
  1284. }
  1285.  
  1286. void homing_think (edict_t *ent)
  1287. {
  1288.     edict_t *target = NULL;
  1289.     edict_t *blip = NULL;
  1290.     vec3_t  targetdir, blipdir;
  1291.     vec_t   speed;
  1292.     
  1293.     while ((blip = findradius(blip, ent->s.origin, 1000)) != NULL)
  1294.     {
  1295.         if (!(blip->svflags & SVF_MONSTER) && !blip->client)
  1296.             continue;
  1297.         if (blip == ent->owner)
  1298.             continue;
  1299.         if (!blip->takedamage)
  1300.             continue;
  1301.         if (blip->health <= 0)
  1302.             continue;
  1303.         if (!visible(ent, blip))
  1304.             continue;
  1305.         if (!infront(ent, blip))
  1306.             continue;
  1307.         VectorSubtract(blip->s.origin, ent->s.origin, blipdir);
  1308.         blipdir[2] += 16;
  1309.         if ((target == NULL) || (VectorLength(blipdir) < VectorLength(targetdir)))
  1310.         {
  1311.             target = blip;
  1312.             VectorCopy(blipdir, targetdir);
  1313.         }
  1314.     }
  1315.     
  1316.     if (target != NULL)
  1317.     {
  1318.         // target acquired, nudge our direction toward it
  1319.         VectorNormalize(targetdir);
  1320.         VectorScale(targetdir, 0.2, targetdir);
  1321.         VectorAdd(targetdir, ent->movedir, targetdir);
  1322.         VectorNormalize(targetdir);
  1323.         VectorCopy(targetdir, ent->movedir);
  1324.         vectoangles(targetdir, ent->s.angles);
  1325.         speed = VectorLength(ent->velocity);
  1326.         VectorScale(targetdir, speed, ent->velocity);
  1327.     }
  1328.     
  1329.     ent->nextthink = level.time + .1;
  1330. }
  1331.  
  1332. void fire_sidewinder (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
  1333. {
  1334.     edict_t    *rocket;
  1335.  
  1336.     rocket = G_Spawn();
  1337.     VectorCopy (start, rocket->s.origin);
  1338.     VectorCopy (dir, rocket->movedir);
  1339.     vectoangles (dir, rocket->s.angles);
  1340.     VectorScale (dir, speed, rocket->velocity);
  1341.     rocket->movetype = MOVETYPE_FLYMISSILE;
  1342.     rocket->clipmask = MASK_SHOT;
  1343.     rocket->solid = SOLID_BBOX;
  1344.     rocket->s.effects |= EF_ROCKET;
  1345.     VectorClear (rocket->mins);
  1346.     VectorClear (rocket->maxs);
  1347.     rocket->s.modelindex = gi.modelindex ("models/objects/sidewind/tris.md2");
  1348.     rocket->owner = self;
  1349.     rocket->touch = rocket_touch;
  1350.     
  1351.     // CCH: if they have 5 cells, start homing, otherwise normal rocket think
  1352.     if (self->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] >= 5)
  1353.     {
  1354.         self->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] -= 5;
  1355.         rocket->nextthink = level.time + .1;
  1356.         rocket->think = homing_think;
  1357.     } 
  1358.     else
  1359.     {
  1360.         gi.cprintf(self, PRINT_HIGH, "No cells for homing missile.\n");
  1361.         rocket->nextthink = level.time + 8000/speed;
  1362.         rocket->think = G_FreeEdict;
  1363.     }
  1364.     
  1365.     rocket->dmg = damage;
  1366.     rocket->radius_dmg = radius_damage;
  1367.     rocket->dmg_radius = damage_radius;
  1368.     rocket->s.sound = gi.soundindex ("weapons/rockfly.wav");
  1369.     rocket->classname = "rocket";
  1370.  
  1371.     if (self->client)
  1372.         check_dodge (self, rocket->s.origin, dir, speed);
  1373.  
  1374.     gi.linkentity (rocket);
  1375. }
  1376.  
  1377. /*
  1378. =================
  1379. fire_floater
  1380. =================
  1381. */
  1382. void floater_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
  1383. {
  1384.     vec3_t        origin;
  1385.     int            n;
  1386.  
  1387.     if (other == ent->owner)
  1388.         return;
  1389.  
  1390.     if (surf && (surf->flags & SURF_SKY))
  1391.     {
  1392.         G_FreeEdict (ent);
  1393.         return;
  1394.     }
  1395.  
  1396.     if (ent->owner->client)
  1397.         PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
  1398.  
  1399.     // calculate position for the explosion entity
  1400.     VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
  1401.  
  1402.     if (other->takedamage)
  1403.     {
  1404.         T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_ROCKET);
  1405.     }
  1406.     else
  1407.     {
  1408.         // don't throw any debris in net games
  1409.         if (!deathmatch->value && !coop->value)
  1410.         {
  1411.             if ((surf) && !(surf->flags & (SURF_WARP|SURF_TRANS33|SURF_TRANS66|SURF_FLOWING)))
  1412.             {
  1413.                 n = rand() % 5;
  1414.                 while(n--)
  1415.                     ThrowDebris (ent, "models/objects/debris2/tris.md2", 2, ent->s.origin);
  1416.             }
  1417.         }
  1418.     }
  1419.  
  1420.     T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_R_SPLASH);
  1421.  
  1422.     gi.WriteByte (svc_temp_entity);
  1423.     if (ent->waterlevel)
  1424.         gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
  1425.     else
  1426.         gi.WriteByte (TE_ROCKET_EXPLOSION);
  1427.     gi.WritePosition (origin);
  1428.     gi.multicast (ent->s.origin, MULTICAST_PHS);
  1429.  
  1430.     G_FreeEdict (ent);
  1431. }
  1432.  
  1433. void fire_floater (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage, float timer)
  1434. {
  1435.     edict_t    *floater;
  1436.     
  1437.     floater = G_Spawn();
  1438.     VectorCopy (start, floater->s.origin);
  1439.     VectorCopy (dir, floater->movedir);
  1440.     vectoangles (dir, floater->s.angles);
  1441.     VectorScale (dir, speed, floater->velocity);
  1442.     floater->movetype = MOVETYPE_FLYMISSILE;
  1443.     floater->clipmask = MASK_SHOT;
  1444.     floater->solid = SOLID_BBOX;
  1445.     floater->s.effects |= EF_ROTATE;
  1446.     floater->s.renderfx |= RF_GLOW;
  1447.     VectorClear (floater->mins);
  1448.     VectorClear (floater->maxs);
  1449.     floater->s.modelindex = gi.modelindex ("models/objects/electroball/tris.md2");
  1450.     floater->owner = self;
  1451.     floater->touch = floater_touch;
  1452.     floater->nextthink = level.time + .1;
  1453.     floater->think = bfg_think;//homing_think;
  1454.     floater->dmg = damage;
  1455.     floater->radius_dmg = radius_damage;
  1456.     floater->dmg_radius = damage_radius;
  1457.     floater->s.sound = gi.soundindex ("weapons/electroball.wav");
  1458.     floater->classname = "floater";
  1459.  
  1460.     if (self->client)
  1461.         check_dodge (self, floater->s.origin, dir, speed);
  1462.     
  1463.     gi.linkentity (floater);
  1464. }
  1465.  
  1466. void fire_floater_alien (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage, float timer)
  1467. {
  1468.     edict_t    *floater;
  1469.     
  1470.     floater = G_Spawn();
  1471.     VectorCopy (start, floater->s.origin);
  1472.     VectorCopy (dir, floater->movedir);
  1473.     vectoangles (dir, floater->s.angles);
  1474.     VectorScale (dir, speed, floater->velocity);
  1475.     floater->movetype = MOVETYPE_FLYMISSILE;
  1476.     floater->clipmask = MASK_SHOT;
  1477.     floater->solid = SOLID_BBOX;
  1478.     floater->s.effects |= EF_ROTATE;
  1479.     floater->s.renderfx |= RF_GLOW;
  1480.     VectorClear (floater->mins);
  1481.     VectorClear (floater->maxs);
  1482.     floater->s.modelindex = gi.modelindex ("models/objects/electroball/tris.md2");
  1483.     floater->owner = self;
  1484.     floater->touch = floater_touch;
  1485.     floater->nextthink = level.time + .1;
  1486.     floater->think = bfg_think_alien;//homing_think;
  1487.     floater->dmg = damage;
  1488.     floater->radius_dmg = radius_damage;
  1489.     floater->dmg_radius = damage_radius;
  1490.     floater->s.sound = gi.soundindex ("weapons/electroball.wav");
  1491.     floater->classname = "floater";
  1492.  
  1493.     if (self->client)
  1494.         check_dodge (self, floater->s.origin, dir, speed);
  1495.     
  1496.     gi.linkentity (floater);
  1497. }
  1498.  
  1499. void fire_stinger2 (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
  1500. {
  1501.     edict_t    *stinger;
  1502.  
  1503.     stinger = G_Spawn();
  1504.     VectorCopy (start, stinger->s.origin);
  1505.     VectorCopy (dir, stinger->movedir);
  1506.     vectoangles (dir, stinger->s.angles);
  1507.     VectorScale (dir, speed, stinger->velocity);
  1508.     stinger->movetype = MOVETYPE_FLYMISSILE;
  1509.     stinger->clipmask = MASK_SHOT;
  1510.     stinger->solid = SOLID_BBOX;
  1511.     stinger->s.effects |= EF_ROCKET;
  1512.     VectorClear (stinger->mins);
  1513.     VectorClear (stinger->maxs);
  1514.     stinger->s.modelindex = gi.modelindex ("models/objects/stinger/tris.md2");
  1515.     stinger->owner = self;
  1516.     stinger->touch = stinger_touch;
  1517.     stinger->nextthink = level.time + .1;
  1518.     stinger->think = homing_think;
  1519.     stinger->dmg = damage;
  1520.     stinger->radius_dmg = radius_damage;
  1521.     stinger->dmg_radius = damage_radius;
  1522.     stinger->s.sound = gi.soundindex ("weapons/rockfly.wav");
  1523.     stinger->classname = "stinger";
  1524.  
  1525.     if (self->client)
  1526.         check_dodge (self, stinger->s.origin, dir, speed);
  1527.  
  1528.     gi.linkentity (stinger);
  1529. }
  1530.  
  1531. void fire_sidewinder2 (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
  1532. {
  1533.     edict_t    *stinger;
  1534.  
  1535.     stinger = G_Spawn();
  1536.     VectorCopy (start, stinger->s.origin);
  1537.     VectorCopy (dir, stinger->movedir);
  1538.     vectoangles (dir, stinger->s.angles);
  1539.     VectorScale (dir, speed, stinger->velocity);
  1540.     stinger->movetype = MOVETYPE_FLYMISSILE;
  1541.     stinger->clipmask = MASK_SHOT;
  1542.     stinger->solid = SOLID_BBOX;
  1543.     stinger->s.effects |= EF_ROCKET;
  1544.     VectorClear (stinger->mins);
  1545.     VectorClear (stinger->maxs);
  1546.     stinger->s.modelindex = gi.modelindex ("models/objects/sidewind/tris.md2");
  1547.     stinger->owner = self;
  1548.     stinger->touch = stinger_touch;
  1549.     stinger->nextthink = level.time + .1;
  1550.     stinger->think = homing_think;
  1551.     stinger->dmg = damage;
  1552.     stinger->radius_dmg = radius_damage;
  1553.     stinger->dmg_radius = damage_radius;
  1554.     stinger->s.sound = gi.soundindex ("weapons/rockfly.wav");
  1555.     stinger->classname = "stinger";
  1556.  
  1557.     if (self->client)
  1558.         check_dodge (self, stinger->s.origin, dir, speed);
  1559.  
  1560.     gi.linkentity (stinger);
  1561. }